package com.seahorse.youliao.security;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.seahorse.youliao.security.handler.MyAuthenticationException;
import com.seahorse.youliao.util.HttpRequestUtil;
import com.seahorse.youliao.utils.SpringContextUtils;
import com.zengtengpeng.operation.RedissonObject;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @ProjectName: youliao
 * @Package: com.seahorse.youliao.security
 * @ClassName: JwtLoginFilter
 * @Description: 启动登录认证流程过滤器
 *  覆写认证方法，修改用户名、密码的获取方式
 *  覆写认证成功后的操作，移除后台跳转，添加生成令牌并返回给客户端
 * @author:songqiang
 * @Date:2020-01-10 9:50
 **/
public class JwtLoginFilter extends UsernamePasswordAuthenticationFilter {


    public JwtLoginFilter(AuthenticationManager authManager,
                          AuthenticationSuccessHandler successHandler,
                          AuthenticationFailureHandler failureHandler,
                          ApplicationEventPublisher eventPublisher) {
        setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/auth/login", "POST"));
        setAuthenticationManager(authManager);
        setAuthenticationSuccessHandler(successHandler);
        setAuthenticationFailureHandler(failureHandler);
        setApplicationEventPublisher(eventPublisher);
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        // POST 请求 /login 登录时拦截， 由此方法触发执行登录认证流程，可以在此覆写整个登录认证逻辑
        super.doFilter(req, res, chain);
    }


    /**
     * 此过滤器的用户名密码默认从request.getParameter()获取，但是这种
     * 读取方式不能读取到如 application/json 等 post 请求数据，需要把
     * 用户名密码的读取逻辑修改为到流中读取request.getInputStream()
     * 在此做验证码的验证
     * @param request
     * @param response
     * @return
     * @throws AuthenticationException
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {

        //获取请求实体
        String body = HttpRequestUtil.getBody(request);
        JSONObject jsonObject = JSON.parseObject(body);

        String uuid = jsonObject.getString("uuid");
        String code = jsonObject.getString("imgCode");

        RedissonObject redissonObject = (RedissonObject)SpringContextUtils.getBeanByClass(RedissonObject.class);

        // 查询验证码
        String redisCode = redissonObject.getValue(uuid);
        // 清除验证码
        redissonObject.delete(uuid);
        if (StringUtils.isBlank(redisCode)) {
            logger.error("验证码不存在或已过期");
            throw new MyAuthenticationException("验证码不存在或已过期");
        }
        if (StringUtils.isBlank(code) || !code.equalsIgnoreCase(redisCode)) {
            logger.error("验证码错误");
            throw new MyAuthenticationException("验证码错误");
        }

        //账户和密码
        String username = jsonObject.getString("username");
        String password = jsonObject.getString("password");

        if (username == null) {
            username = "";
        }

        if (password == null) {
            password = "";
        }

        username = username.trim();
        JwtAuthenticatioToken authRequest = new JwtAuthenticatioToken(username, password);
        // Allow subclasses to set the "details" property
        setDetails(request, authRequest);

        return this.getAuthenticationManager().authenticate(authRequest);

    }

}
